import uuid
import logging
import urllib
import urlparse
import cgi
import sys
import traceback

from google.appengine.ext import db
from google.appengine.api import urlfetch
from django.utils import simplejson as json
from hashlib import sha1
from hmac import new as hmac
import random
import md5
from time import time

from impact import platform

API_EXCEPTION_CODE_AUTH_ERROR      = 0x10
API_EXCEPTION_CODE_BAD_SELECTOR    = 0x20
API_EXCEPTION_CODE_BAD_REQUEST     = 0x30
API_EXCEPTION_CODE_API_LIMIT	   = 0x40
API_EXCEPTION_CODE_SERVICE_ERROR   = 0x50
API_EXCEPTION_CODE_UNKNOWN_ERROR   = 0x60
API_EXCEPTION_CODE_TIMEOUT         = 0x70

config	    = None
def init(app_config):
	global config
	config 		= app_config['authorization']

def log_debug(msg):
	if config['debug']:
		logging.info(__name__+": "+msg)


def get_url_mappings():
	return[
		('/authorizations', Authorizations),
		('/authorizations/do_authorization', DoAuthorization),
		('/authorize/Facebook',FacebookAuthorizationHandler),
		('/authorize/Twitter',TwitterAuthorizationHandler),
		('/authorize/YouTube',YouTubeAuthorizationHandler),
		('/authorize/GoogleAnalytics',GoogleAnalyticsAuthorizationHandler),
		('/authorize/Vimeo',VimeoAuthorizationHandler),
		('/api/authorization/(.*)',AuthorizationApi)
		]

class OAuth1BaseHandler(platform.ImpactBaseHandler):
	class api:
		def __init__(self,api_endpoint,api_call_style,api_endpoint_json_config,api_endpoint_headers,api_endpoint_params,api_endpoint_error_check_f,consumer_key,consumer_secret,access_token,access_token_secret):
			self.api_endpoint                = api_endpoint
			self.api_call_style              = api_call_style
			self.api_endpoint_json_config    = api_endpoint_json_config
			self.api_endpoint_headers        = api_endpoint_headers
			self.api_endpoint_params         = api_endpoint_params
			self.api_endpoint_error_check_f  = api_endpoint_error_check_f
			self.consumer_key                = consumer_key
			self.consumer_secret             = consumer_secret
			self.access_token                = access_token
			self.access_token_secret         = access_token_secret
			self.profile					 = {}

		def call(self,path_or_method,params=None,method=urlfetch.GET,headers=None,use_endpoint_as_base=True):
			if params is None:
				params = {}
			if headers is None:
				headers= {}
			try:
				response = self.call_async(path_or_method,params,method,headers,use_endpoint_as_base).get_result()
				if response.status_code != 200:
					logging.info("!!!API path= "+self.api_endpoint+path_or_method)
					logging.info("!!!API response_code was "+str(response.status_code)+" content-"+str(response.content))
					raise platform.ImpactException("Call to "+self.api_endpoint +" method: "+ path_or_method+" with params "+str(params)+" failed. Status code was "+str(response.status_code),response.status_code)
				else:
					try:
						response_dict = json.loads(response.content)
						#logging.info("API RESPONSE CONTENT WAS "+response.content)
					except Exception,e:
						raise platform.ImpactException("JSON Decode of Call to "+self.api_endpoint +" method: "+ path_or_method+" with params "+str(params)+" failed. ("+str(response.status_code)+")",response.status_code)
					if self.api_endpoint_error_check_f != None:
						self.api_endpoint_error_check_f(response_dict)
					return response_dict
			except platform.ImpactException:
				raise
			except Exception,e:
				logging.info("!!!API EXCEPTION response_code was "+str(response.status_code)+" content-"+str(response.content))
				raise platform.ImpactException("Call to "+self.api_endpoint +" method: "+path_or_method+" with params "+str(params)+" failed. "+str(e))

		def call_async(self,path_or_method,params=None,method=urlfetch.GET,headers=None,use_endpoint_as_base=True):
			if params is None:
				params = {}
			if headers is None:
				headers= {}
			call_url = self.api_endpoint
			if use_endpoint_as_base:
				if self.api_call_style['type'] == 'path':
						call_url = call_url + path_or_method
			else:
				call_url = path_or_method

			#if method name is passed as a param instead as part of path
			if self.api_call_style['type'] == 'param':
				params[self.api_call_style['param_name']] = path_or_method

			#config request for json
			if self.api_endpoint_json_config['type'] == 'param':
				params[self.api_endpoint_json_config['param_name']] = self.api_endpoint_json_config['param_value']
			elif self.api_endpoint_json_config['type'] == 'extension':
				call_url = call_url +'.'+ self.api_endpoint_json_config['extension']

			#add additional headers
			if self.api_endpoint_headers:
				headers.update(self.api_endpoint_headers)

			#add additional params
			if self.api_endpoint_params:
				params.update(self.api_endpoint_params)

			rpc = OAuth1BaseHandler.make_async_oauth_request(call_url, self.consumer_key, self.consumer_secret, self.access_token_secret, {'oauth_token':self.access_token}, params, method, headers)
			return rpc


	def get_type(self):
		pass
	def get_consumer_key(self):
		pass
	def get_consumer_secret(self):
		pass
	def get_request_url(self):
		pass
	def get_authorize_url(self):
		pass
	def get_xtra_authorize_params(self):
		return None
	def get_access_url(self):
		pass
	def get_user_profile_info(self,access_token,access_token_secret):
		pass
	def get_preferred_auth_flow_method(self):
		return urlfetch.POST
	def get_scope(self,request):
		return request.get("scope")
	#api metadata
	def get_api_call_endpoint(self):
		pass
	def get_api_call_style(self):
		return {'type':'path'}
	def get_api_call_json_config(self):
		return {'type':'param','param_name':'format','param_value':'json'}
	def get_api_call_headers(self):
		return None
	def get_api_call_params(self):
		return None
	def get_api_call_check_error_on_200_f(self):
			#see Vimeo for an example
		return None
	def get_api_call_translate_non_200_response_f(self):
			#see Twitter for an example
		return None

	def create_api(self,*args):
		#args[0] is access_token and args[1] is access_token secret and args[3] is profile dict
		return OAuth1BaseHandler.api(self.get_api_call_endpoint(),self.get_api_call_style(),self.get_api_call_json_config(),self.get_api_call_headers(),self.get_api_call_params(),self.get_api_call_check_error_on_200_f(),self.get_consumer_key(),self.get_consumer_secret(),args[0],args[1])

	def process(self,uctx,args=None):
		if self.request.get("oauth_verifier"):
			oauth_token         = self.request.get("oauth_token")
			dbtid               = self.request.get('dbtid')
			temp_token          = TempUserAuthToken.get_by_key_name(dbtid)
			try:
				response            = self.make_oauth_request(self.get_access_url(), temp_token.secret,{'oauth_token':oauth_token,'oauth_verifier':self.request.get('oauth_verifier')},None,self.get_preferred_auth_flow_method())
			except Exception ,e:
				raise e
			if response.status_code != 200:
				raise platform.ImpactException("<pre>Problem getting access token: "+response.content+"</pre>")
			else:
				response_dict       = cgi.parse_qs(response.content)
				access_token        = response_dict["oauth_token"][-1]
				access_token_secret = response_dict["oauth_token_secret"][-1]
				try:
					profile             =    self.get_user_profile_info(access_token, access_token_secret)
				except Exception, e:
					traceback.print_exc()
					self.response.out.write("<PRE>LOOKUP OF USERINFO FAILED\n\n+"+str(e)+"</PRE>");
					return
				#might want to find a better uid
				username = profile['username']
				uid      = profile['uid']
				pic      = profile['pic']
				#save auth with user
				user = temp_token.user
				parent		 = None
				if temp_token.pkey is not None:
					parent	 = db.get(temp_token.pkey)

				log_debug("CALLING USER OBJECT IS "+str(user))
				log_debug("USERNAME IS "+username)
				log_debug("UID IS "+uid)
				log_debug("PIC IS "+pic)

				#look for existing integration point
				existing_ip_for_account =  db.Query(IntegrationPointInstance).ancestor(parent).filter("type =",self.get_type()).filter("uid =",uid).filter("user =",user).fetch(1)
				if len(existing_ip_for_account) == 0:
					log_debug("NO EXISTING INTEGRATION POINT OF TYPE "+self.get_type()+ "CREATING ONE WITH "+access_token+" secret:"+access_token_secret+" FOR USER "+str(user))
					ip = IntegrationPointInstance(parent=parent, type=self.get_type(), token=access_token, secret=access_token_secret, username=username, uid=uid, pic=pic)
				else:
					log_debug("FOUND EXISTING INTEGRATION POINT OF TYPE "+self.get_type()+ "UPDATING WITH "+access_token+" secret:"+access_token_secret+" FOR USER "+str(user))
					ip              =   existing_ip_for_account[0]
					ip.token 		=   access_token
					ip.secret       =   access_token_secret
					ip.username     =   username
					ip.pic          =   pic

				ip.save(user)

				if temp_token.redirect is not None:
					log_debug("!!REDIRECTING TO "+temp_token.redirect)
					self.redirect(temp_token.redirect+'?ip='+str(ip.key()))
				else:
					self.response.out.write(get_auth_complete_js(ip));
				temp_token.delete();
		else:
			parts        = urlparse.urlparse(self.request.path_url)
			redirect_uri = parts.scheme+"://"+parts.netloc+parts.path
			return_to    = self.request.get('r')
			#log_debug("!!!RETURN TO IS "+return_to)
			pkey	 = self.request.get('p')
			temp_token   = TempUserAuthToken(user=uctx.user, key_name=str(uuid.uuid4()), redirect=return_to, pkey=pkey)
			temp_token.put()

			redirect_uri = redirect_uri + "?dbtid="+temp_token.key().name()
			scope = self.get_scope(self.request)
			if scope:
				additional_params = {'scope':scope}
			else:
				additional_params = {}

			response = self.make_oauth_request(self.get_request_url(), "", {'oauth_callback':redirect_uri},additional_params,self.get_preferred_auth_flow_method())
			if response.status_code != 200:
				self.response.out.write("<pre>Problem getting access token: "+response.content+"</pre>")
			else:
				response_dict           =  cgi.parse_qs(response.content)
				oauth_token             = response_dict["oauth_token"][-1]
				oauth_token_secret      = response_dict["oauth_token_secret"][-1]

				temp_token.secret       = oauth_token_secret
				temp_token.put()

				#got the access token and secret..send them to to authorize
				authorize_qs = "?oauth_token="+oauth_token
				if self.get_xtra_authorize_params() != None:
						for k,v in self.get_xtra_authorize_params().items():
								authorize_qs = authorize_qs +"&"+k+"="+v
				self.redirect(self.get_authorize_url()+authorize_qs)


	def make_signed_request(self,url,token,secret,additional_params=None,method=urlfetch.POST,headers=None):
		if headers is None:
			headers = {}
		return self.make_oauth_request(url,secret,{"oauth_token":token},additional_params,method,headers)

	def make_async_signed_request(self,url,token,secret,additional_params=None,method=urlfetch.POST,headers=None):
		if headers is None:
			headers = {}
		return OAuth1BaseHandler.make_async_oauth_request(url,self.get_consumer_key(),self.get_consumer_secret(),secret,{"oauth_token":token},additional_params,method,headers)

	def make_oauth_request(self,url,secret,oauth_params,additional_params=None,method=urlfetch.POST,headers=None):
		if headers is None:
			headers = {}
		return OAuth1BaseHandler.make_async_oauth_request(url,self.get_consumer_key(),self.get_consumer_secret(), secret, oauth_params, additional_params, method, headers).get_result()

	@staticmethod
	def make_async_oauth_request(url,consumer_key,consumer_secret,secret,oauth_params,additional_params=None,method=urlfetch.POST,headers=None):
		if headers is None:
			headers = {}
		def encode(text):
			return urllib.quote(str(text), "~")

		def generate_nonce():
			random_number = ''.join(str(random.randint(0, 9)) for i in range(40))
			m = md5.new(str(time()) + str(random_number))
			return m.hexdigest()


		oauth_params["oauth_consumer_key"]       = consumer_key
		oauth_params["oauth_signature_method"]   = "HMAC-SHA1"
		oauth_params["oauth_version"]            = "1.0"

		if not 'oauth_timestamp' in oauth_params:
			oauth_params['oauth_timestamp'] = str(int(time()))
		if not 'oauth_nonce' in oauth_params:
			oauth_params['oauth_nonce'] = str(generate_nonce())


		all_params = dict(oauth_params);
		if additional_params:
			all_params.update(additional_params)

		#need to utf-8 encode stuff in signing string
		for k,v in all_params.items():
			if isinstance(v, unicode):
				all_params[k] = v.encode('utf8')

		# Join all of the params together.
		params_str = "&".join(["%s=%s" % (encode(k), encode(all_params[k]))
													 for k in sorted(all_params)])

		# Join the entire message together per the OAuth specification.
		message = "&".join(["GET" if method == urlfetch.GET else "POST",
											 encode(url), encode(params_str)])
		log_debug('!!!BASE STRING IS '+message)

		# Create a HMAC-SHA1 signature of the message.
		log_debug("!!!CONSUMER SECRET IS "+consumer_secret+" SECRET IS "+str(secret))
		key = "%s&%s" % (encode(consumer_secret), encode(secret)) # Note compulsory "&".
		signature = hmac(key, message, sha1)
		digest_base64 = signature.digest().encode("base64").strip()

		oauth_params["oauth_signature"] = digest_base64
		auth_header = ", ".join(["%s=\"%s\"" % (encode(k), encode(oauth_params[k]))
													 for k in sorted(oauth_params)])

		auth_header = "OAuth " + auth_header
		headers["Authorization"] = auth_header
		log_debug("!!!AUTH HEADER IS "+auth_header)
		log_debug("!!!URL IS "+url)

		rpc = urlfetch.create_rpc(deadline=10.0)
		if method == urlfetch.GET:
			if additional_params:
				url  = "%s?%s" % (url,urllib.urlencode(additional_params))
			log_debug("GET url is "+url)
			urlfetch.make_fetch_call(rpc, url, method=urlfetch.GET, headers=headers)
		else:
			payload = None
			if additional_params:
				payload =urllib.urlencode(additional_params)
			urlfetch.make_fetch_call(rpc, url, method=urlfetch.POST, headers=headers,payload=payload)

		return rpc;

class YouTubeAuthorizationHandler(OAuth1BaseHandler):
	""" http://code.google.com/apis/gdata/articles/oauth.html """
	def get_type(self):
		return "YouTube"
	def get_consumer_key(self):
		return config['keys']['gdata']['consumer_key']
	def get_consumer_secret(self):
		return config['keys']['gdata']['consumer_secret']
	def get_request_url(self):
		return "https://www.google.com/accounts/OAuthGetRequestToken"
	def get_authorize_url(self):
		return "https://www.google.com/accounts/OAuthAuthorizeToken"
	def get_access_url(self):
		return "https://www.google.com/accounts/OAuthGetAccessToken"
	def get_preferred_auth_flow_method(self):
		return urlfetch.GET
	def get_scope(self,request):
		return 'https://gdata.youtube.com'

#api metadata
	def get_api_call_endpoint(self):
		return "https://gdata.youtube.com/feeds/api"
	def get_api_call_json_config(self):
		return {'type':'param','param_name':'alt','param_value':'json'}
	def get_api_call_headers(self):
		return {"X-GData-Key": "key="+config['keys']['gdata']['youtube_developer_key']}
	def get_api_call_params(self):
		return {"v":"2"}
	def get_api_call_check_error_on_200_f(self):
		return None
	def get_api_call_translate_non_200_response_f(self):
		#def f(code,response_dict):
		# 400 response code identifies a bad request. For example, you will receive a 400 response code if you submit a request to the wrong URL or include an unsupported or nonexistent parameter in your request. The API response content will explain the reason wny the API returned a 400 response code.
		#*401 (Not authorized) - A 401 response code indicates that a request did not contain an Authorization header, that the format of the Authorization header was invalid, or that the authentication token supplied in the header was invalid.
		#*403 (Forbidden) - A 403 response code indicates that you have submitted a request that is not properly authenticated for the requested operation.
		#*500 (Internal error) - A 500 response code indicates that YouTube experienced an error handling a request. You could retry the request at a later time.
		#*501 (Not implemented) - A 501 response code indicates that you have tried to execute an unsupported operation, such as retrieving a list of ratings or updating a complaint.
		#*503 (Service unavailable) - A 503 response code indicates that the YouTube Data API service can not be reached. You could retry your request at a later time.

		#API_EXCEPTION_CODE_AUTH_ERROR
		#API_EXCEPTION_CODE_BAD_SELECTOR
		#API_EXCEPTION_CODE_BAD_REQUEST
		#API_EXCEPTION_CODE_RATE_EXCEEDED
		#API_EXCEPTION_CODE_SERVICE_ERROR
		#API_EXCEPTION_CODE_UNKNOWN_ERROR
		#return f
		return None

	def get_user_profile_info(self,access_token,access_token_secret):
		api     = self.create_api(access_token,access_token_secret)
		profile = api.call('/users/default')
		profile['username'] = profile['entry']['yt$username']['$t']
		profile['uid']      = profile['entry']['id']['$t']
		profile['pic']      = profile['entry']['media$thumbnail']['url']
		return profile

class GoogleAnalyticsAuthorizationHandler(OAuth1BaseHandler):
	""" http://code.google.com/apis/gdata/articles/oauth.html """
	def get_type(self):
		return "GoogleAnalytics"
	def get_consumer_key(self):
		return config['keys']['gdata']['consumer_key']
	def get_consumer_secret(self):
		return config['keys']['gdata']['consumer_secret']
	def get_request_url(self):
		return "https://www.google.com/accounts/OAuthGetRequestToken"
	def get_authorize_url(self):
		return "https://www.google.com/accounts/OAuthAuthorizeToken"
	def get_access_url(self):
		return "https://www.google.com/accounts/OAuthGetAccessToken"
	def get_preferred_auth_flow_method(self):
		return urlfetch.GET
	def get_scope(self,request):
		return 'https://www.google.com/analytics/feeds/'

	#api metadata
	def get_api_call_endpoint(self):
		return "https://www.google.com/analytics/feeds"
	def get_api_call_json_config(self):
		return {'type':'param','param_name':'alt','param_value':'json'}
	def get_api_call_params(self):
		return {"v":"2"}
	def get_api_call_translate_non_200_response_f(self):
		#def f(code,response_dict):
		#201 CREATED 	Creation of a resource was successful.
		#304 NOT MODIFIED 	The resource hasn't changed since the time specified in the request's If-Modified-Since header.
		#400 BAD REQUEST 	Invalid request URI or header, or unsupported nonstandard parameter.
		#??rate limit as well??
		#401 UNAUTHORIZED 	Authorization required.
		#403 FORBIDDEN 	Unsupported standard parameter, or authentication or authorization failed.
		#404 NOT FOUND 	Resource (such as a feed or entry) not found.
		#409 CONFLICT 	Specified version number doesn't match resource's latest version number.
		#420 Rate limit???
		#410 GONE 	Requested change history is no longer available on the server. Refer to service-specific documentation for more details.
		#500 INTERNAL SERVER ERROR 	Internal error. This is the default code that is used for all unrecognized server errors.
		#return f
		return None
	def get_user_profile_info(self,access_token,access_token_secret):
		api = self.create_api(access_token,access_token_secret)
		profile = api.call("/accounts/default")
		profile['username'] = profile['feed']['title']['$t']
		profile['uid']      = profile['feed']['id']['$t']
		profile['pic']      = 'http://www.google.com/images/logos/analytics_logo.gif'
		return profile

# call https://gdata.youtube.com/feeds/api/users/default?alt=json
# username ["feed"]["title"] : { "$t" : "Profile list for topherlafata@gmail.com" }
# id ["feed"]["id"] : { "$t" : "http://www.google.com/analytics/feeds/accounts/topherlafata@gmail.com" },
# pic http://www.google.com/images/logos/analytics_logo.gif

class TwitterAuthorizationHandler(OAuth1BaseHandler):
	""" http://code.google.com/apis/gdata/articles/oauth.html """
	#auth metadata
	def get_type(self):
		return "Twitter"
	def get_consumer_key(self):
		return config['keys']['twitter']['consumer_key']
	def get_consumer_secret(self):
		return config['keys']['twitter']['consumer_secret']
	def get_request_url(self):
		return "https://api.twitter.com/oauth/request_token"
	def get_authorize_url(self):
		return "https://api.twitter.com/oauth/authorize"
	def get_access_url(self):
		return "https://api.twitter.com/oauth/access_token"
	def get_preferred_auth_flow_method(self):
		return urlfetch.POST
	def get_scope(self,request):
		return None

	#api metadata
	def get_api_call_endpoint(self):
		return 'http://api.twitter.com/1'
	def get_api_call_json_config(self):
		return {'type':'extension','extension':'json'}
	def get_api_call_translate_non_200_response_f(self):
		#def f(code,response_dict):
				#* 304 Not Modified: There was no new data to return.
				#* 400 Bad Request: The request was invalid.  An accompanying error message will explain why. This is the status code will be returned during rate limiting.
				#* 401 Unauthorized: Authentication credentials were missing or incorrect.
				#* 403 Forbidden: The request is understood, but it has been refused.  An accompanying error message will explain why. This code is used when requests are being denied due to update limits.
				#* 404 Not Found: The URI requested is invalid or the resource requested, such as a user, does not exists.
				#* 406 Not Acceptable: Returned by the Search API when an invalid format is specified in the request.
				#* 420 Enhance Your Calm: Returned by the Search and Trends API  when you are being rate limited.
				#* 500 Internal Server Error: Something is broken.  Please post to the group so the Twitter team can investigate.
				#* 502 Bad Gateway: Twitter is down or being upgraded.
				#* 503 Service Unavailable: The Twitter servers are up, but overloaded with requests. Try again later.
		#API_EXCEPTION_CODE_AUTH_ERROR
		#API_EXCEPTION_CODE_BAD_SELECTOR
		#API_EXCEPTION_CODE_BAD_REQUEST
		#API_EXCEPTION_CODE_RATE_EXCEEDED
		#API_EXCEPTION_CODE_SERVICE_ERROR
		#API_EXCEPTION_CODE_UNKNOWN_ERROR
		#return f
		return None

	def get_user_profile_info(self,access_token,access_token_secret):
		api = self.create_api(access_token,access_token_secret)
		profile = api.call('/account/verify_credentials')
		profile['username'] = profile['name']
		profile['uid']      = profile['id_str']
		profile['pic']      = profile['profile_image_url']
		return profile


class VimeoAuthorizationHandler(OAuth1BaseHandler):
	""" http://code.google.com/apis/gdata/articles/oauth.html """
	def get_type(self):
		return "Vimeo"
	def get_consumer_key(self):
		return config['keys']['vimeo']['consumer_key']
	def get_consumer_secret(self):
		return config['keys']['vimeo']['consumer_secret']
	def get_request_url(self):
		return "http://vimeo.com/oauth/request_token"
	def get_authorize_url(self):
		return "http://vimeo.com/oauth/authorize"
	def get_access_url(self):
		return "http://vimeo.com/oauth/access_token"
	def get_xtra_authorize_params(self):
		return {'permission':'read'}
	def get_preferred_auth_flow_method(self):
		return urlfetch.GET
	def get_scope(self,request):
		return None

	#api metadata
	def get_api_call_endpoint(self):
		return "http://vimeo.com/api/rest/v2"
	def get_api_call_style(self):
		return {'type':'param','param_name':'method'}
	def get_api_call_json_config(self):
		return {'type':'param','param_name':'format','param_value':'json'}
	def get_api_call_check_error_on_200_f(self):
		def f(response_dict):
			if 'err' in response_dict:
				raise platform.ImpactException(response_dict['err']['expl'])
		return f
	#not even sure if vimeo does this yet but should be prepared for 400s and 500s
	def get_api_call_translate_non_200_response_f(self):
		#def f(code,response_dict):
				#* 404 Not Found: The URI requested is invalid or the resource requested, such as a user, does not exists.
				#* 500 Internal Server Error: Something is broken.  Please post to the group so the Twitter team can investigate.

		#API_EXCEPTION_CODE_AUTH_ERROR
		#API_EXCEPTION_CODE_BAD_SELECTOR
		#API_EXCEPTION_CODE_BAD_REQUEST
		#API_EXCEPTION_CODE_RATE_EXCEEDED
		#API_EXCEPTION_CODE_SERVICE_ERROR
		#API_EXCEPTION_CODE_UNKNOWN_ERROR
		#return f
		return None

	def get_user_profile_info(self,access_token,access_token_secret):
		api = self.create_api(access_token,access_token_secret)
		profile = api.call('vimeo.people.getInfo')
		profile['username'] = profile['person']['display_name']
		profile['uid']      = profile['person']['id']
		profile['pic']      = profile['person']['portraits']['portrait'][0]["_content"]
		return profile

class FacebookAuthorizationHandler(platform.ImpactBaseHandler):
	#START OAUTH 2 TYPE STUFF
	#this stuff could be broken out into an OAuth 2 handler with
	#more complexity once we have another integratin point requiring oauth 2
	#for now this is mostly tailored to facebook and makes some assumptions
	#about json being returned, authorize params etc

	class api:
		def __init__(self,api_endpoint,api_endpoint_headers,api_endpoint_params,api_endpoint_error_check_f,access_token):
			self.api_endpoint               = api_endpoint
			self.api_endpoint_headers       = api_endpoint_headers
			self.api_endpoint_params        = api_endpoint_params
			self.api_endpoint_error_check_f = api_endpoint_error_check_f
			self.access_token               = access_token
			self.profile					= {}

		def call(self,api_call,params=None,method=urlfetch.GET,headers=None):
			if headers is None:
				headers = {}
			if params is None:
				params = {}
			try:
				response = self.call_async(api_call,params,method,headers).get_result()
				if response.status_code != 200:
					platform.ImpactException("Call to "+self.api_endpoint +" method: "+ api_call+" with params "+str(params)+" failed. ",response.status_code)
				else:
					response_dict = json.loads(response.content)
					if self.api_endpoint_error_check_f != None:
						self.api_endpoint_error_check_f(response_dict)
					return response_dict
			except platform.ImpactException:
					raise
			except Exception,e:
					raise platform.ImpactException("Call to "+self.api_endpoint +" method: "+api_call+" with params "+str(params)+" failed. "+str(e))

		def call_async(self,api_call,params=None,method=urlfetch.GET,headers=None):
			if headers is None:
				headers = {}
			if params is None:
				params = {}
			call_url = self.api_endpoint+api_call
			params['access_token'] = self.access_token

			#add additional headers
			if self.api_endpoint_headers:
				headers.update(self.api_endpoint_headers)

			#add additional params
			if self.api_endpoint_params:
				params.update(self.api_endpoint_params)

			log_debug("!!! ABOUT TO MAKE FB API CALL "+call_url+" HTTP METHOD IS "+str(method)+" PARAMS IS "+str(params)+" HEADERS IS "+str(headers))
			rpc = FacebookAuthorizationHandler.make_async_request(call_url,params, method, headers)
			return rpc

	def create_api(self,*args):
		return FacebookAuthorizationHandler.api(self.get_api_endpoint(),self.get_api_call_headers(),self.get_api_call_params(),self.get_api_call_check_error_on_200_f(),args[0])

	def make_request(self,url,params=None,method=urlfetch.GET,headers=None):
		if headers is None:
			headers = {}
		if params is None:
			params = {}
		return FacebookAuthorizationHandler.make_async_request(url, params, method, headers).get_result()

	@staticmethod
	def make_async_request(url,params=None,method=urlfetch.GET,headers=None):
		if headers is None:
			headers = {}
		if params is None:
			params = {}
		rpc = urlfetch.create_rpc(deadline=10.0)
		if method == urlfetch.GET:
			if params:
				url  = "%s?%s" % (url,urllib.urlencode(params))
			log_debug("GET url is "+url)
			urlfetch.make_fetch_call(rpc, url, method=urlfetch.GET, headers=headers)
		else:
			payload = None
			if params:
					payload =urllib.urlencode(params)
			urlfetch.make_fetch_call(rpc, url, method=urlfetch.POST, headers=headers,payload=payload)
		return rpc;


	#END BEGINNING OF OAUTH 2 TYPE STUFF
	def get_type(self):
		return "Facebook"
	def get_app_id(self):
		return config['keys']['facebook']['app_id']
	def get_app_secret(self):
		return config['keys']['facebook']['app_secret']
	def get_access_url(self):
		return "https://graph.facebook.com/oauth/access_token"
	def get_authorize_url(self):
		return "https://graph.facebook.com/oauth/authorize"
	def get_scope(self,request):
		return request.get("scopes")

	#api meta data
	def get_api_endpoint(self):
		return "https://graph.facebook.com"
	def get_api_call_headers(self):
		return None
	def get_api_call_params(self):
		return None
	def get_api_call_check_error_on_200_f(self):
		def f(response_dict):
			if 'error' in response_dict:
				if 'message' in response_dict['error']:
					raise platform.ImpactException(response_dict['error']['message'])
				else:
					raise platform.ImpactException("Unknown Facebook Error")
		return f
		#
		#{
		#  "error": {
		#    "type": "SomeBizarreTypeOfException",
		#    "message": "Everything went wrong."
		#  }
		#}
		#Note that FacebookGraphException is a catchall Graph API exception. For your convenience, RestFB will throw more-specific subclasses FacebookOAuthException and FacebookQueryParseException if it detects either of these Graph API error types. These are described below.
		# FacebookOAuthException
		#Thrown when the Graph API returns an OAuth-related error (type OAuthException or OAuthAccessTokenException), as shown in the example JSON snippet below.
		#{
		#  "error": {
		#    "type": "OAuthException",
		#    "message": "Invalid access token signature."
		#  }
		#}
		# FacebookQueryParseException
		#Thrown when the Graph API returns an FQL query parsing error (type QueryParseException), as shown in the example JSON snippet below.
		#
		#{
		#  "error": {
		#    "type": "QueryParseException",
		#    "message": "Unknown path components: /fizzle"
		#  }
		#}

		return None
	def get_user_info(self,access_token):
		api     = self.create_api(access_token)
		profile = api.call('/me')
		#response    = urlfetch.fetch("https://graph.facebook.com/me?" +urllib.urlencode(dict(access_token=access_token)))
		profile['username'] = profile['name']
		profile['uid']      = profile['id']
		profile['pic']      = "http://graph.facebook.com/"+profile['uid']+"/picture"
		return profile

	def process(self,uctx,params=None):

		parts = urlparse.urlparse(self.request.path_url)
		base_uri = parts.scheme+"://"+parts.netloc+parts.path
		params = dict()
		if self.request.get("code"):
			dbtid                   = self.request.get("dbtid")
			params['client_id']     = self.get_app_id()
			params['client_secret'] = self.get_app_secret()
			params['code']          = self.request.get("code")
			params['redirect_uri']  = base_uri+"?dbtid="+dbtid
			response = self.make_request(self.get_access_url(), params)
			if response.status_code != 200:
				self.response.out.write("<pre>Problem getting access token: "+response.content+"</pre>")
				return

			response_dict           = cgi.parse_qs(response.content)
			access_token            = response_dict["access_token"][-1]

			#get user info
			profile                 = self.get_user_info(access_token)

			#save auth with user
			temp_token  = TempUserAuthToken.get_by_key_name(dbtid)
			user        = temp_token.user
			parent		 = None
			if temp_token.pkey is not None:
				parent	 = db.get(temp_token.pkey)

			#look for existing integration point
			existing_ip_for_account =  db.Query(IntegrationPointInstance).ancestor(parent).filter("user =",user).filter("type =",self.get_type()).filter("uid =",profile['uid']).fetch(1)
			if len(existing_ip_for_account) == 0:
				ip = IntegrationPointInstance(parent=parent,type=self.get_type(),token=access_token,username=profile['username'],uid=profile['uid'],pic=profile['pic'])
			else:
				ip              =   existing_ip_for_account[0]
				ip.token 		=   access_token
				ip.username     =   profile['username']
				ip.pic          =   profile['pic']

			ip.save(user)
			if temp_token.redirect is not None:
				log_debug("!!REDIRECTING TO "+temp_token.redirect)
				self.redirect(temp_token.redirect+'?ip='+str(ip.key()))
			else:
				self.response.out.write(get_auth_complete_js(ip));
			temp_token.delete();

		else:
			return_to    = self.request.get('r')
			log_debug("!!!RETURN TO IS "+return_to)
			pkey	 = self.request.get('p')
			temp_token              = TempUserAuthToken(user=uctx.user,key_name=str(uuid.uuid4()),redirect=return_to,pkey=pkey)
			temp_token.put()
			params['client_id']     = self.get_app_id()
			if self.get_scope(self.request) is not None:
				params['scope']         = self.get_scope(self.request)
			params['display']       = "popup"
			params['redirect_uri']  = base_uri+"?dbtid="+temp_token.key().name()
			self.redirect(self.get_authorize_url()+"?" +urllib.urlencode(params))



def get_auth_complete_js(ip):
	ip.secret = None
	ip.token = None
	return "<script>window.opener.authorization_complete("+platform.util.entity_to_json(ip)+");this.close()</script>"


#need to make a cron to clean these things up
class TempUserAuthToken(db.Model):
	pkey			= db.StringProperty()
	created         = db.DateTimeProperty(auto_now_add=True)
	user            = db.ReferenceProperty(platform.UserProfile)
	secret          = db.StringProperty()
	redirect		= db.StringProperty()



class IntegrationPointInstance(platform.ImpactModel):
	type   = db.StringProperty()
	token  = db.StringProperty()
	secret = db.StringProperty()
	scopes = db.StringListProperty(default=None)#empty list
	username = db.StringProperty()
	uid      = db.StringProperty()
	pic      = db.StringProperty()

	mask = ('token','secret')

	def get_api(self):
		type            = self.type
		auth_classname  = self.type+"AuthorizationHandler"
		auth_class      = getattr(sys.modules[__name__],auth_classname)
		c               = auth_class()
		api             = c.create_api(self.token,self.secret)
		api.profile		= {'username':self.username,'uid':self.uid,'pic':self.pic}
		return api
	

#api
class AuthorizationApi(platform.ServiceHandler):

	def __init__(self):
		pass

	def get_authorizations(self,uctx,args,params):
		query = db.Query(IntegrationPointInstance)
		query.filter('user =',uctx.user)
		query.order('type')
		return platform.util.query_to_dict_list(query)

	def delete_authorization(self,uctx,args,params):
		key = args[0]
		key = db.Key(encoded=key)
		instance = db.get(key)
		instance.delete()
		return instance.to_dict()

	def test(self,uctx,args,params):
		ip      = db.Query(IntegrationPointInstance).filter("user =",uctx.user).filter("type =","Twitter").fetch(1)[0]
		api     = ip.get_api()
		return api.call('/account/verify_credentials')

#pages
class Authorizations(platform.TemplateHandler):

	def __init__(self):
		self.template = "templates/authorizations.html"

	def prepare_page(self,uctx,inputs,pagedata):
		pagedata['in_production']     = platform.util.in_production()
		pagedata['in_development']     = platform.util.in_development()


class DoAuthorization(platform.ImpactBaseHandler):

	def __init__(self):
		pass

	def process(self,uctx,args):
		dict  = self.get_input_dict()
		type  = dict.get('type')
		self.redirect('/authorize/'+type)

class DoAuthorizationFromInstance(platform.ImpactBaseHandler):

	def __init__(self):
		pass

	def process(self,uctx,args):
		dict  = self.get_input_dict()
		type  = dict.get('type')
		self.redirect('/authorize/'+type)
